﻿using System;
using System.Collections.Generic;
using System.Text;

using Aspects.PInvoke;

namespace Aspects.Unit
{
    public class MagStripeReader
    {
        public string Track1 { get; private set; }
        public string Track2 { get; private set; }
        public string Track3 { get; private set; }
        public string Track4 { get; private set; }
        public string FirstName { get; private set; }
        public string MiddleName { get; private set; }
        public string LastName { get; private set; }
        public string Account { get; private set; }
        public string Expiration { get; private set; }
        public string ServiceCode { get; private set; }
        public string ComPort { get; private set; }
        public bool DataReady { get; private set; }

        private List<byte> InputData = new List<byte>();
        public MagStripeReader()
        {
            ComPort = Native.GetIniFileString(Properties.Settings.Default.AspectsConfigFile, UnitModel.GetModel(), "MSR", "");
        }

        internal void ReadCard(byte[] buffer)
        {
            foreach (byte b in buffer)
            {
                if (b == 0x02)
                {
                    InputData.Clear();
                    DataReady = false;
                }
                InputData.Add(b);

                if (b == 0x03)
                    PopulateData();
            }

        }

        private void PopulateData()
        {
            //Track 1 information
            try
            {
                var start = InputData.IndexOf(0x25, 0);
                var end = InputData.IndexOf(0x3F, start);
                var length = end - start -1;
                byte[] TempData = new byte[79]; //max length of Track1 is 79 characters
                InputData.CopyTo(start + 1, TempData, 0, length);
                ParseTrack1(TempData);
                Track1 = Encoding.ASCII.GetString(TempData);
            }
            catch (Exception ex)
            {
                Console.WriteLine("Error parsing track1.");
                Console.WriteLine(ex.Message);
            }
            //Track 2 information
            try
            {
                var start = InputData.IndexOf(0x3B, 0);
                var end = InputData.IndexOf(0x3F, start);
                var length = end - start - 1;
                byte[] TempData = new byte[40]; //max length of Track2 is 40 characters
                InputData.CopyTo(start + 1, TempData, 0, length);
                Track2 = Encoding.ASCII.GetString(TempData);
            }
            catch (Exception ex)
            {
                Console.WriteLine("Error parsing track2.");
                Console.WriteLine(ex.Message);
            }
            //Track 3 information
            try
            {
                var start = InputData.IndexOf(0x2B, 0);
                var end = InputData.IndexOf(0x3F, start);
                var length = end - start - 1;
                byte[] TempData = new byte[40]; //TODO find length of track 3
                InputData.CopyTo(start + 1, TempData, 0, length);
                Track3 = Encoding.ASCII.GetString(TempData);
            }
            catch
            {
                Track3 = "No Data / Bad Read";
            }
            //Track 4 information
            Track4 = "Not Implemented Yet";
            /*
            try
            {

            }
            catch
            {

            }
             */

            DataReady = true;

        }

        private void ParseTrack1(byte[] track1)
        {
            //FC = Format Code. Check if it is present on card
            bool FC = (track1[0] == 0x42) || (track1[0] == 0x41);
            var index = FC ? 1 : 0; //start either at 1 or 0 depending on Format code

            Account = GetString(track1, ref index, 19, 0x5E);
            LastName = GetString(track1, ref index, 26, 0x2F);
            FirstName = GetString(track1, ref index, 26, 0x5E);
            if (FirstName.Contains(" "))
            {
                MiddleName = FirstName.Split(' ')[1].Trim();
                FirstName = FirstName.Split(' ')[0].Trim();
            }
            Expiration = GetString(track1, ref index, 4, 0xFF);
            ServiceCode = GetString(track1, ref index, 3, 0xFF);

        }

        private string GetString(byte[] track, ref int index, int max, byte tester)
        {
            var tempString = new StringBuilder();
            for (int i = 0; i < max; i++, index++)
            {
                if (track[index] != tester) //character '/' for name seperator
                    tempString.Append((char)track[index]);
                else
                {
                    index++;
                    break;
                }
            }
            return tempString.ToString();

        }
        /*
         * The Track 1 structure is specified as:
         * (The maximum record length is 79 alphanumeric characters.)
         * STX : Start sentinel "%"
         * FC : Format code "B" (The format described here. Format "A" is reserved for proprietary use.)
         * PAN : Primary Account Number, up to 19 digits
         * FS : Separator "^"
         * NM : Name, 2 to 26 characters (including separators, where appropriate, between surname, first name etc.)
         * FS : Separator "^"
         * ED : Expiration Data, 4 digits or "^"
         * SC : Service code, 3 digits or "^"
         * DD : Discretionary Data, balance of characters
         * ETX : End sentinel "?"
         * LRC : Longitudinal redundancy check, calculated according to ISO/IEC 7811-2
         * 
         * The Track 2 structure is specified as:
         * (The maximum record length is 40 numeric digits.)
         * STX : Start sentinel ";"
         * PAN : Primary Account Number, up to 19 digits, as defined in ISO/IEC 7812-1
         * FS : Separator "="
         * ED : Expiration date, YYMM or "=" if not present
         * SC : Service code, 3 digits or "=" if not present
         * DD : Discretionary Data, balance of available digits
         * ETX : End sentinel "?"
         */
    }
}
